// var 的变量提升在前，还是 function 函数声明在前 
function f() {
  var a = 1
  // console.log('g', g) // f () {} 说明函数提升在前,由于对后面的代码执行逻辑有影响，暂且先注释掉
  // var g = 'g'
  a = 2
  var b = g() // 调用的时候 a 是 2，g函数return a
  a = 3
  return b

  function g() {
    return a
  }
}
console.log('f result', f()) // 2

// 变量提升
function f2(boolean) {
  if (boolean) {
    var x = 10 // if 并没有作用域,这里会提升到函数作用域最前面
  }
  return x
}
console.log(f2(true)) // 10
console.log(f2(false)) // return undefined

// var 声明的变量在多次声明同一个变量时并不会导致错误
// 目的是想实现字符串的叠加.用 var 声明 for 循环中的变量会出
// 现问题，用 let 声明 for 循环中的变量，就会如期望的那样打
// 印，因为 let 是声明了一个新的变量，每次的 i 都是一个新的，
// 外层内层都如此
var arr = [
  'lee',
  'lee',
  'lee',
  'lee',
  //  'lee'
]

function sumMatrix(arr) {
  // 在 arr 刚好是4个元素的时候,内层循环完毕刚好跳出循环。但
  // 是当 arr 时五个元素的时候，这个函数将会进入死循环。因为
  // 都是引用的同一个 i，到了内层 i 又重新被赋值为了0,然后到
  // 外层循环，i = 4,内层循环又是 0,这样循环往复，将永远达不到结束循环的条件
  var sum = 'm'
  for (var i = 0; i < arr.length; i++) {
    var currentRow = arr[i]
    console.log('currentRow', currentRow)
    for (var i = 0; i < currentRow.length; i++) {
      sum = currentRow[i] + sum
    }
    console.log(i)
  }
  return sum
}
console.log('sumMatrix', sumMatrix(arr)) // eelm

// var声明变量在捕获变量时的怪异之处 归根结底，时setTimeout回调函数里引用的都是同一个i，延时过后
// i已经变为了10 
for (var i = 0; i < 10; i++) {
  setTimeout(function () {
    console.log(i)
  }, 100) // 十个10， 因为i是全局的，i在跳出循环的时候是10
}

for (var k = 0; k < 10; k++) {
  setTimeout(() => {
    console.log(k)
  }, 100) // 也是十个10 也就是说箭头函数时没用的
}

// 如果想让其按照期望去打印，需要让回调函数中的i是不同的i

// 用es5创建块级作用域的方式，用一个立即执行函数来捕获每一个i
for (var i = 0; i < 10; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(i)
    }, 100) //打印出0 -9,到十的时候，跳出循环，所以不打印出10
  }(i))
}


// 块作用域
if (true) {
  let a = 1
}
try {
  console.log(a) // a is not definedc
} catch (e) {
  console.log(e)
}

function foo() {
  return letVo
}
let letVo = 1
foo() // 在变量声明之后调用函数，这样没有问题
// 但是
try {
  function foo() {
    return letVo
  }
  foo()
  let letVo = 1

} catch (e) {
  console.log(e) // Cannot access 'letVo' before initialization
}

// 用var 声明变量是，无论声明多少次，最终引用的都是同一个变量
function f(x) {
  var x 
  var x
  if (true) {
    var x
  }
}
// 但是使用let就不会那么宽松,不允许在同一作用域内let const声明同一个变量
try {
  function hh () {
    let x = 1
    // let x = 2 // 报错
  }
} catch (e) {
  console.log(e)
}
try {
  function hh () {
    let x = 1
    // var x = 2 // 报错  var 跟let混用也不行
  }
} catch (e) {
  console.log(e)
}

// 函数参数并不是用let声明的，可以跟let声明的同时存在
function inser (x) {
  if (x) {
    let x = 100
    return x
  }
  return x
}
console.log(inser(false)) // false
console.log(inser(true)) // 100

// 在一个嵌套作用域里引入一个新名字的行为称作屏蔽。通常来讲，应该避免使用频闭，因为要写出清晰的代码。但是有时候也会用到。
// 用let重写之前的嵌套循环,利用频闭是的内外层i互不干扰


function sumMatrix1 (arr) {
  let sum = 'm'
  for (let i = 0; i < arr.length; i ++) {
    var currentRow = arr[i]
    for (let i = 0; i < currentRow.length; i++) {
      sum = currentRow[i] + sum
    }
  }
  return sum
}
console.log('sumMatrix1', sumMatrix1(arr))

function theCity () {
  let getCity
  if (true) {
    let city = 'city'
    getCity = function () {
      return city
    }
  }
  return getCity()
}
console.log(theCity()) // city

function theCity () {
  let city = 'city';
  return function () {
    return city;
  }
}
console.log('theCity', theCity()()) // city

// let 解决for循环中的问题
for (let i = 0; i < 10; i++) {
  setTimeout(function() {
    console.log(i)
  }, 100) // 可以按期望打印
}